/*
Copyright (c) 2008,2009, David Beck

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "secobj_priv.hh"
#include "mpool.hh"
#include <string.h>

/**
  @file secobj_priv.cc
  @brief implementation of secobj_priv
*/

#ifndef RT0D4_RX_SECOBJ_PRIV_GEN_ITEM_SZ
#define RT0D4_RX_SECOBJ_PRIV_GEN_ITEM_SZ 64
#endif /* RT0D4_RX_SECOBJ_PRIV_GEN_ITEM_SZ */

using rt0d4::common::mpool;

namespace rt0d4
{
  namespace rx
  {
    /** @brief Private implementation of secobj_priv */
    struct secobj_priv::impl
    {
      /* typedefs */
      typedef void * vptr_t;

      /* variables */
      bool                  is_server_;
      char **               algs_;
      int                   min_eckey_strength_;
      int                   max_eckey_strength_;
      bool                  encrypt_packets_;
      validate_peer_cert    validate_peer_cert_cb_;
      validate_peer_credentials validate_peer_credentials_cb_;
      destroy_conn_data     destroy_conn_data_cb_;
      create_conn_data      create_conn_data_cb_;
      unsigned char *       own_cert_;
      size_t                own_cert_len_;
      char *                own_login_;
      char *                own_pass_;
      size_t                debug_;
      mpool<>               p_gen_;

      /* initialization */
      impl(bool is_server)
        : is_server_(is_server),
          algs_(0),
          min_eckey_strength_(0),
          max_eckey_strength_(100000),
          encrypt_packets_(true),
          validate_peer_cert_cb_(0),
          validate_peer_credentials_cb_(0),
          destroy_conn_data_cb_(0),
          create_conn_data_cb_(0),
          own_cert_(0),
          own_cert_len_(0),
          own_login_(0),
          own_pass_(0),
          debug_(0)
      { }

      /* private functions */
      void checked_free(vptr_t ptr)
      {
        if( !ptr ) return;
        if( !p_gen_.free( ptr ) ) free( ptr );
        ptr = 0;
      }

      char *
      gen_alloc(size_t len)
      {
        return (char *)p_gen_.allocate( len );
      }

      unsigned char *
      gen_udup(const unsigned char * ptr, size_t len)
      {
        unsigned char * ret = (unsigned char *)gen_alloc( len );
        memcpy( ret,ptr,len );
        return ret;
      }

      char *
      str_dup( const char * str )
      {
        size_t sz = strlen( str );
        char * ret = (char *)gen_alloc( sz + 1 );
        memcpy(ret,str,sz);
        ret[sz] = 0;
        return ret;
      }

      /* interface */
      bool is_server() { return is_server_; }

      bool add_algoritms(const char ** algs)
      {
        if( !algs ) return false;
        const char ** p = algs;
        size_t n=0;
        /* count how many algoritthms given */
        while( *p ) { ++n; ++p; }

        if( n )
        {
          /* alloc space */
          algs_ = (char **)gen_alloc( sizeof(char *)*(n+1) );

          /* fill a copy */
          size_t i=0;
          for( i=0;i<n;++i ) algs_[i] = str_dup( algs[i] );
          algs_[i] = NULL;

          return true;
        }
        return false;
      }

      void set_own_cert(const unsigned char * p, size_t len)
      {
        if( !p || !len ) return;
        checked_free( (void *)own_cert_ );
        own_cert_ = gen_udup( p, len );
        own_cert_len_ = len;
      }

      void set_own_credentials(const char * login, const char * pass)
      {
        if( login )
        {
          checked_free( (void *)own_login_ );
          own_login_ = str_dup( login );
        }
        if( pass )
        {
          checked_free( (void *)own_pass_ );
          own_pass_  = str_dup( pass );
        }
      }

      const char ** get_algorithms() const
      {
        return (const char **)algs_;
      }

      bool set_eckey_strength(int min, int max)
      {
        if( min > max ) return false;
        min_eckey_strength_ = min;
        max_eckey_strength_ = max;
        return true;
      }

      void get_eckey_strength(int & min, int & max) const
      {
        min = min_eckey_strength_;
        max = max_eckey_strength_;
      }

      int get_eckey_strength_min() const { return min_eckey_strength_; }
      int get_eckey_strength_max() const { return max_eckey_strength_; }

      void encrypt_packets(bool yesno) { encrypt_packets_ = yesno; }
      bool encrypt_packets() const     { return encrypt_packets_; }

      void set_peer_cert_validation_cb( validate_peer_cert cert_validate_cb )
      {
        validate_peer_cert_cb_ = cert_validate_cb;
      }

      void set_peer_credentials_validation_cb( validate_peer_credentials creds_validate_cb )
      {
        validate_peer_credentials_cb_ = creds_validate_cb;
      }

      validate_peer_cert get_peer_cert_validation_cb() const
      {
        return validate_peer_cert_cb_;
      }

      validate_peer_credentials get_peer_credentials_validation_cb() const
      {
        return validate_peer_credentials_cb_;
      }

      void set_destroy_conn_data_cb( destroy_conn_data destroy_conn_data_cb )
      {
        destroy_conn_data_cb_ = destroy_conn_data_cb;
      }
    
      void set_create_conn_data_cb( create_conn_data create_conn_data_cb )
      {
        create_conn_data_cb_ = create_conn_data_cb;
      }

      destroy_conn_data get_destroy_conn_data_cb() const
      {
        return destroy_conn_data_cb_;
      }

      create_conn_data get_create_conn_data_cb() const
      {
        return create_conn_data_cb_;
      }

      const unsigned char * get_own_cert(size_t & len) const
      {
        if( own_cert_len_ > 0 )
        {
          len = own_cert_len_;
          return own_cert_;
        }
        return 0;
      }

      const char * get_own_login() const { return own_login_; }
      const char * get_own_pass() const  { return own_pass_;  }

      void set_debug(size_t debug_lifetime) { debug_ = debug_lifetime; }

      bool do_debug()
      {
        if( debug_ > 0 )
        {
          --debug_;
          return true;
        }
        return false;
      }
    };

    /* public interface */
    bool
    secobj_priv::is_server()
    {
      return impl_->is_server();
    }

    bool
    secobj_priv::add_algoritms(const char ** algs)
    {
      return impl_->add_algoritms(algs);
    }

    const char **
    secobj_priv::get_algorithms() const
    {
      return impl_->get_algorithms();
    }

    bool
    secobj_priv::set_eckey_strength(int min, int max)
    {
      return impl_->set_eckey_strength(min,max);
    }

    void
    secobj_priv::get_eckey_strength(int & min, int & max) const
    {
      impl_->get_eckey_strength(min,max);
    }

    int
    secobj_priv::get_eckey_strength_min() const
    {
      return impl_->get_eckey_strength_min();
    }

    int
    secobj_priv::get_eckey_strength_max() const
    {
      return impl_->get_eckey_strength_max();
    }

    void
    secobj_priv::encrypt_packets(bool yesno)
    {
      impl_->encrypt_packets(yesno);
    }

    bool
    secobj_priv::encrypt_packets() const
    {
      return impl_->encrypt_packets();
    }

    void
    secobj_priv::set_peer_cert_validation_cb( validate_peer_cert cert_validate_cb )
    {
      impl_->set_peer_cert_validation_cb( cert_validate_cb );
    }

    void
    secobj_priv::set_peer_credentials_validation_cb( validate_peer_credentials creds_validate_cb )
    {
      impl_->set_peer_credentials_validation_cb( creds_validate_cb );
    }

    validate_peer_cert
    secobj_priv::get_peer_cert_validation_cb() const
    {
      return impl_->get_peer_cert_validation_cb();
    }

    validate_peer_credentials
    secobj_priv::get_peer_credentials_validation_cb() const
    {
      return impl_->get_peer_credentials_validation_cb();
    }
        
    void
    secobj_priv::set_destroy_conn_data_cb( destroy_conn_data destroy_conn_data_cb )
    {
      impl_->set_destroy_conn_data_cb( destroy_conn_data_cb );
    }
    
    void
    secobj_priv::set_create_conn_data_cb( create_conn_data create_conn_data_cb )
    {
      impl_->set_create_conn_data_cb( create_conn_data_cb );
    }

    destroy_conn_data 
    secobj_priv::get_destroy_conn_data_cb() const
    {
      return impl_->get_destroy_conn_data_cb();
    }

    create_conn_data 
    secobj_priv::get_create_conn_data_cb() const
    {
      return impl_->get_create_conn_data_cb();
    }

    void
    secobj_priv::set_own_cert(const unsigned char * p, size_t len)
    {
      impl_->set_own_cert(p,len);
    }

    const unsigned char *
    secobj_priv::get_own_cert(size_t & len) const
    {
      return impl_->get_own_cert(len);
    }

    void
    secobj_priv::set_own_credentials(const char * login, const char * pass)
    {
      impl_->set_own_credentials(login, pass);
    }

    const char *
    secobj_priv::get_own_login() const
    {
      return impl_->get_own_login();
    }

    const char *
    secobj_priv::get_own_pass() const
    {
      return impl_->get_own_pass();
    }

    void
    secobj_priv::set_debug(size_t debug_lifetime)
    {
      impl_->set_debug(debug_lifetime);
    }

    bool
    secobj_priv::do_debug()
    {
      return impl_->do_debug();
    }

    secobj_priv::secobj_priv(bool is_server)
      : impl_(new impl(is_server)) {}

    secobj_priv::~secobj_priv() {}

    /* private functions, copying not allowed */
    secobj_priv::secobj_priv()
      : impl_(new impl(false)) {}

    secobj_priv::secobj_priv(const secobj_priv & other)
      : impl_((impl *)0) { throw std::string("should never be called"); }

    secobj_priv &
    secobj_priv::operator=(const secobj_priv & other)
    {
      throw std::string("should never be called");
      return *this;
    }
  };
};

/* EOF */
